
#include "CentralCache.h"
#include "PageCache.h"
CentralCache CentralCache::_sInst;

size_t CentralCache::FetchRangeObj(void *&start, void *&end, size_t batchNum, size_t size)
{
    size_t index = SizeClass::Index(size);
    _spanLists[index]._mtx.lock();

    auto span = GetOneSpan(_spanLists[index], size);
    assert(span && span->_freeList);

    // 从 span中拿取对象，不够的话，有多少拿多少
    start = span->_freeList;
    end = start;
    int actualNum = 1;
    for (int i = 0; i < batchNum - 1 && NextObj(end); ++i)
    {
        end = NextObj(end);
        ++actualNum;
    }
    span->_freeList = NextObj(end);
    NextObj(end) = nullptr;
    _spanLists[index]._mtx.unlock();
    return actualNum;
}

Span *CentralCache::GetOneSpan(SpanList &list, size_t size)
{
    Span *it = list.Begin();
    // 查看当前的spanlist中是否具有未分配对象的span
    while (it != list.End())
    {
        if (it->_freeList)
        {
            return it;
        }
        else
        {
            it = it->_next;
        }
    }
    // 走到这里，先把桶锁解开，这样，其他的线程再进行空间是释放的时候就不会阻塞！
    list._mtx.unlock(); // !对应到后面的

    // 现在spanlist中没有未被分配的对象，需要去pageCache中找对象
    PageCache::GetInstance()->_pageMtx.lock();
    auto span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));
    PageCache::GetInstance()->_pageMtx.unlock();

    assert(span);
    // 对获取的 span进行切分，不需要加锁，因为其他的线程也访问不到

    // 计算span的大块内存的起始地址和大块内存的大小（字节）
    char* start = (char *)((span->_pageId) << PAGE_SHIFT);
    size_t bytes = span->_n << PAGE_SHIFT;
    char *end = start + bytes;
    // 把大块的内存切成自由链表连接起来
    // 1. 先切一块下去做头，方便尾插
    span->_freeList = start;
    start += size;
    void *tail = span->_freeList;

    while (start < end)
    {
        NextObj(tail) = start;
        tail = NextObj(tail);
        start += size;
    }

    // 切好 span之后，需要把span挂到桶里面
    list._mtx.lock();
    list.PushFront(span);
    // 把大块内存，切成自由链表挂起来
    return span;
}
void CentralCache::ReleaseListToSpans(void *start, size_t size)
{
    size_t index = SizeClass::Index(size);
    _spanLists[index]._mtx.lock();
    while (start)
    {
        void* next = NextObj(start);
        Span* span = PageCache::GetInstance()->MapObjectToSpan(start);
        NextObj(start) = span->_freeList;
        span->_freeList = start;
        span->_useCount--;
        if (!span->_useCount)
        {
            // 说明 span的 切分出去的所有小块内存都回来了
            // 这个 span 就可以在回收回来给 page cache ,再尝试前后页合并
            _spanLists[index].Erase(span);
            span->_freeList = nullptr;
            span->_next = nullptr;
            span->_prev = nullptr;

            // 在释放 span给pagecache时,使用 pageCahe的 Mutex就够了
            // 这个时候可以把桶锁解开
            _spanLists[index]._mtx.unlock();
            PageCache::GetInstance()->_pageMtx.lock();
            PageCache::GetInstance()->ReleaseSpanToPagecache(span);
            PageCache::GetInstance()->_pageMtx.unlock();
            _spanLists[index]._mtx.lock();

        }
        start = next;
    }
    _spanLists[index]._mtx.unlock();

}
